/**
  ******************************************************************************
  * @file    sccb.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "bsp.h"
#include "tskcfg.h"
#include "ov2640.h"

#define SCCB_SDA_Pin          GPIO_PIN_4
#define SCCB_SDA_LL_Pin       LL_GPIO_PIN_4
#define SCCB_SDA_GPIO_Port    GPIOD                       
#define sda_h()               (SCCB_SDA_GPIO_Port->BSRR = SCCB_SDA_Pin)
#define sda_l()               (SCCB_SDA_GPIO_Port->BRR = SCCB_SDA_Pin)
#define sda_r()               LL_GPIO_IsInputPinSet(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin)//(SCCB_SDA_GPIO_Port->IDR & SCCB_SDA_Pin)

#define SCCB_SCL_Pin          GPIO_PIN_5
#define SCCB_SCL_LL_Pin       LL_GPIO_PIN_5
#define SCCB_SCL_GPIO_Port    GPIOD
#define scl_h()               (SCCB_SCL_GPIO_Port->BSRR = SCCB_SCL_Pin)
#define scl_l()               (SCCB_SCL_GPIO_Port->BRR = SCCB_SCL_Pin)

#define SCCB_ID   			      0x60


void SCCB_BspInit(void)
{											      	 
	LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD);
  
  LL_GPIO_ResetOutputPin(OV2640_RESET_GPIO_Port, OV2640_RESET_LL_Pin);
  LL_GPIO_SetOutputPin(OV2640_PWDN_GPIO_Port, OV2640_PWDN_LL_Pin);
  
  GPIO_InitStruct.Pin = OV2640_PWDN_LL_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
  LL_GPIO_Init(OV2640_PWDN_GPIO_Port, &GPIO_InitStruct);
  
  GPIO_InitStruct.Pin = OV2640_RESET_LL_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  LL_GPIO_Init(OV2640_RESET_GPIO_Port, &GPIO_InitStruct);  
  
  GPIO_InitStruct.Pin = SCCB_SDA_LL_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  LL_GPIO_Init(SCCB_SDA_GPIO_Port, &GPIO_InitStruct);
  
  GPIO_InitStruct.Pin = SCCB_SCL_LL_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  LL_GPIO_Init(SCCB_SCL_GPIO_Port, &GPIO_InitStruct);
}			 

static inline void sda_to_in(void)
{
  LL_GPIO_SetPinPull(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_PULL_UP);
	LL_GPIO_SetPinMode(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_MODE_INPUT);
}
 
static inline void sda_to_out(void)
{
  LL_GPIO_SetPinPull(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_PULL_UP);
	LL_GPIO_SetPinMode(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_MODE_OUTPUT);
  LL_GPIO_SetPinOutputType(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinSpeed(SCCB_SDA_GPIO_Port, SCCB_SDA_LL_Pin, LL_GPIO_SPEED_FREQ_HIGH);
}

static inline void sccb_delay(void)
{
  volatile uint16_t delay_time = 442;
	    
	while(delay_time--); /* 8~359k */
}

static inline void delay_100us(void)
{
  volatile uint16_t delay_time = 887;
	    
	while(delay_time--); /* 8~359k */
}

static inline void sccb_start(void)
{
  sda_h();
  scl_h();
  sccb_delay();
  
  sda_l();
  sccb_delay();
  
  scl_l();
}

static inline void sccb_stop(void)
{
  sda_l();
  sccb_delay();
  scl_h();
  sccb_delay();
  
  sda_h();
  sccb_delay();
}

static inline uint8_t sccb_write_byte(uint8_t dat)
{
	volatile uint8_t i, ack;
  
  scl_l();                   /* 保证scl处于低 */
  
	for(i=0; i<8; i++)
	{
    if(dat & 0x80)
    {
      sda_h();
    }
		else
    {
      sda_l();
    }
    
		dat<<=1;
    
    sccb_delay();
		scl_h();
    
    sccb_delay();
		scl_l();
	}

	sda_to_in();    /*释放data*/
  sccb_delay();
  
	scl_h();        /*第9个时钟的上升沿*/
	sccb_delay();

  if(sda_r() != 0)
	{
		ack = 1;
	}
	else
	{
		ack = 0;
	}

	scl_l();		 
	sda_to_out();
  
	return ack;  
}	 

static inline uint8_t sccb_read_byte(void)
{
	volatile uint8_t i;
	volatile uint8_t dat;
	
	dat = 0;
	
	scl_l();             /* 保证scl处于低 */
	
	sda_to_in();
	
	for(i=0; i<8; i++)
	{
    sccb_delay();
		scl_h();                  /* scl拉高后,读出数据 */
		
		dat <<= 1;
		
		if(sda_r() != 0)
		{
			dat |= 0x01u;
		}
		else
		{
			dat &= 0xfeu;
		}
    
		sccb_delay();
		scl_l();                  /* scl拉低,待对方准备数据 */
	}
	
	sda_to_out();
  sccb_delay();
  
  sda_h();  /* set 1 */
  
  scl_h();
  sccb_delay();              /*发送ack*/
	
  scl_l();
	sccb_delay();              /* 字节间延迟 */
  
  sda_l();	
  sccb_delay();
  
	return dat;
}

uint8_t SCCB_WriteReg(uint8_t reg, uint8_t data)
{
  uint8_t res=0;
  
	sccb_start();
  
	if(sccb_write_byte(SCCB_ID))
  {
    res=1;
  }
  
  if(sccb_write_byte(reg))
  {
    res=1;
  }
  
  if(sccb_write_byte(data))
  {
    res=1;
  }
  
  sccb_stop();
  
  return	res;
}		  					    

uint8_t SCCB_ReadReg(uint8_t reg)
{
  uint8_t val=0;

  sccb_start();

  sccb_write_byte(SCCB_ID);

  sccb_write_byte(reg);

  sccb_stop();


	sccb_start();
  
	sccb_write_byte(SCCB_ID|0x01u);
  
  val=sccb_read_byte();
  
  sccb_stop();
  
  return val;
}
